/*
 * Copyright (C) 2011-2013 Eric Lasota
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#ifndef __RDX_ILOPCODES_HPP__
#define __RDX_ILOPCODES_HPP__

#include "rdx_basictypes.hpp"

namespace RDX
{
	namespace ILOpcodes
	{
		const int NUM_OPTIMIZED_MOVES	= 24;

		static const LargeInt ILOP_MOVE_DEST_PARENT_FRAME	= 1;	// Destination is relative to PRV instead of BP
		static const LargeInt ILOP_MOVE_DEST_DEREF			= 2;	// Destination is a pointer that needs to be dereferenced
		static const LargeInt ILOP_MOVE_DEST_OBJECT			= 4;	// Destination is inside of an object and source export should not optimize memory
		static const LargeInt ILOP_MOVE_SRC_PARENT_FRAME	= 8;	// Source is relative to PRV instead of BP
		static const LargeInt ILOP_MOVE_SRC_DEREF			= 16;	// Source is a pointer that needs to be dereferenced
		static const LargeInt ILOP_MOVE_SRC_TRANSIENT		= 32;	// Source dies after this op, so it may be optimized out if the previous op stored it
		static const LargeInt ILOP_MOVE_SRC_TYPE_DEFAULT	= 64;	// Source is relative to NULL, not the local frame, and contains a sixth parameter indicating the type it defaulted from
		static const LargeInt ILOP_MOVE_SRC_OBJECT			= 128;	// Source is inside of an object and source export should not optimize memory

		// NOTE:
		// OP_property can potentially come after instructions emitting any of:
		// - ILOP_incptr (OP_property)
		// - ILOP_pinp (OP_pinlocal)
		// - ILOP_arrayindex (OP_arrrayindex)
		// - ILOP_objinterior (OP_property)
		// All of these must always emit the offset as their final parameter
		enum ILOpcode
		{
			ILOP_invalid,

			ILOP_debuginfo,			// <filename> <linenum>                                 Does nothing, used for debug information
			ILOP_move,				// <src> <dest> <size> <align> <loc flags>              Copy current frame to current frame.  Size may be zero.

			ILOP_tovarying,			// <dest> <type> <src>                                  Converts a runtime pointer at the destination to a typed runtime pointer
			ILOP_tovarying_static,	// <loc> <type>                                         Converts a runtime pointer at the destination to a typed runtime pointer in the same location

			ILOP_pinl,				// <dest> <offset>                                      Creates a pointer to the current frame + offset
			ILOP_pinp,				// <dest> <offset>                                      Creates a pointer to the parent frame + offset
			ILOP_incptr,			// <loc> <offset>                                       Increases a pointer's size by offset
			ILOP_objinterior,		// <src> <dest> <offset>                                NULL check src and create a pointer to the start of src + offset
			ILOP_objinterior_notnull,//<src> <dest> <offset>                                Creates a pointer to the start of src + offset
			ILOP_objinterior_notnull_persist,//												Same as ILOP_objinterior_notnull, but source isn't transient

			ILOP_immediate,			// <LA0:value> <LA1[0]:dest> <LA1[1]:size>              Copy instruction value to current frame
			ILOP_immediate_ptr,		// <LA0:value> <LA1[0]:dest> <LA1[1]:size>              Copy instruction value to current frame (immediate is a pointer to an object)
			ILOP_immediate_rtp,		// <LA0:value> <LA1[0]:dest> <LA1[1]:size>              Copy instruction value to current frame (immediate is a runtime pointer to an object)

			ILOP_arrayindex,		// <indexsrc> <arraysrc> <dest> <offset>                NULL check arraysrc from current frame, push internal reference to array head + offset

			// NOTE: Frameoffs is always aligned to RDX_MAX_ALIGNMENT
			ILOP_call,				// <frameoffs> <prvoffs> <method>                       Transfers control to the target method
			ILOP_calldelegatebp,	// <frameoffs> <prvoffs> <methodsrc>                    Null checks and transfers control to the target method.  methodsrc is bp-relative
			ILOP_calldelegateprv,	// <frameoffs> <prvoffs> <methodsrc>                    Null checks and transfers control to the target method.  methodsrc is prv-relative
			ILOP_callvirtual,		// <frameoffs> <prvoffs> <objsrc> <(I)vstidx>           Loads the target method, null checks, and transfers control
			ILOP_callinterface,		// <frameoffs> <prvoffs> <objsrc> <(I)vstidx> ...       Loads the target method, null checks, and transfers control.  Method is for source export only
									//     ... <interface> <method>

			ILOP_verifynotnull,		// <loc> <instr offset>                                 Verify that a current-frame value is not null, if not, move forward a set number of instructions and throw an exception
			ILOP_zero,				// <loc> <size>                                         Zero fill a location in the current frame (opstack aligned)
			ILOP_newinstance,		// <loc> <type> <dimsrc> <ndim> <stride>                Creates a new instance of the specified type in the current frame

			ILOP_exit,				//                                                      Exits the current frame

			ILOP_jump,				// <instrnum>                                           Jumps to the specified instruction
			ILOP_jtrue,				// <instrnum> <src>                                     Jumps to the specified instruction if the target value is true
			ILOP_jfalse,			// <instrnum> <src>                                     Jumps to the specified instruction if the target value is not true
			ILOP_jinherits,			// <instrnum> <src> <type>                              Jumps to the specified instruction if the target value inherits from the specified type

			ILOP_tick,				//														Decrements the timeout counter, suspends the thread if timeout hits zero
			ILOP_assertenum,		// <src> <type>                                         Throws IncompatibleConversionException if value in current frame can not be found in a specified enum table.
			ILOP_assertinherits,	// <src> <type>                                         Throws IncompatibleConversionException if value in current frame can not be cast to type.  Value may be null.
			ILOP_hash_f,			// <src> <dest> <size> <align>                          Generates a hashcode from the current frame and stores it at dest
			ILOP_hash_p,			// <src> <dest> <size> <align>                          Generates a hashcode from a pointer on current frame and stores it at dest

			ILOP_jeq_f,				// <instrnum> <src1> <src2> <size> <align>              Jumps to the specified instruction if the two specified values are equal
			ILOP_jeq_p,				// <instrnum> <src1> <src2> <size> <align>              Jumps to the specified instruction if the two specified pointers are equal
			ILOP_jne_f,				// <instrnum> <src1> <src2> <size> <align>              Jumps to the specified instruction if the two specified values are not equal
			ILOP_jne_p,				// <instrnum> <src1> <src2> <size> <align>              Jumps to the specified instruction if the two specified pointers are not equal

			ILOP_xnullref,			//                                                      Throws a NullReferenceException
			ILOP_catch,				//                                                      Throws an InvalidOperationException, but internally, marks the next instruction as patchable
			ILOP_fatal,				//                                                      Throws an InvalidOperationException
			ILOP_throw,				// <src>                                                Throws the exception at src.  If src is null, throws UnspecifiedException

			ILOP_hardenstack,		//                                                      Do not allow the next instruction to optimize transient storage

			ILOP_switch,			// <src> <array res> <count> <size> <align>             Searches array for the value at src, if successful jumps the number of instructions, otherwise jumps <count>
			ILOP_switch_ptr,		// <src> <array res> <count> <size> <align>             Searches array for the value referenced by a pointer at src, if successful jumps the number of instructions, otherwise jumps <count>
			ILOP_iteratearray,		// <array> <index> <dest> <exit> <size> <align>         If index is within the array bounds, copies the array element at that index to dest and increments it, otherwise jumps to exit
			ILOP_iteratearraysub,	// <array> <index> <dest> <exit> <size> <align>
									// ... <subidx loc> <num subidx>                        Same as ILOP_iteratearray, but also increments subindexes

			ILOP_intrinsic,

			// Function intrinsics are always <parameters> <return values>
			// Branch intrinsics are always <target> <parameters>
			// Unlike calls, parameters are not guaranteed to be aligned to RDX_MAX_ALIGNMENT
			ILOP_firstintrinsic = ILOP_intrinsic,

			ILOP_iadd,		// Adds two integers of size p2
			ILOP_isub,		// Subtracts two integers of size p2
			ILOP_imul,		// Multiplies two integers of size p2
			ILOP_idiv,		// Divides two integers of size p2
			ILOP_imod,		// Returns the modulo of two integers of size p2
			ILOP_ineg,		// Negates an integer of size p2
			ILOP_isx,		// Converts a signed integer of size p2 to a signed integer of size p3
			ILOP_itof,		// Converts a signed integer of size p2 to a float of size p3

			ILOP_ilt,		// Compares two integers of size p2, jumps to p3 if a < b
			ILOP_igt,		// Compares two integers of size p2, jumps to p3 if a > b
			ILOP_ile,		// Compares two integers of size p2, jumps to p3 if a <= b
			ILOP_ige,		// Compares two integers of size p2, jumps to p3 if a >= b
			ILOP_ieq,		// Compares two integers of size p2, jumps to p3 if a == b
			ILOP_ine,		// Compares two integers of size p2, jumps to p3 if a != b

			ILOP_fadd,		// Adds two floats of size p2
			ILOP_fsub,		// Subtracts two floats of size p2
			ILOP_fmul,		// Multiplies two floats of size p2
			ILOP_fdiv,		// Divides two floats of size p2
			ILOP_fneg,		// Negates an floats of size p2
			ILOP_ftoi,		// Converts a float of size p2 to a signed integer of size p3
			
			ILOP_flt,		// Compares two integers of size p2, jumps to p3 if a < b
			ILOP_fgt,		// Compares two integers of size p2, jumps to p3 if a > b
			ILOP_fle,		// Compares two integers of size p2, jumps to p3 if a <= b
			ILOP_fge,		// Compares two integers of size p2, jumps to p3 if a >= b
			ILOP_feq,		// Compares two integers of size p2, jumps to p3 if a == b
			ILOP_fne,		// Compares two integers of size p2, jumps to p3 if a != b

			ILOP_jccp,		// Jumps if a conversion check from p1 to p2 passes
			ILOP_jccf,		// Jumps if a conversion check from p1 to p2 fails

			ILOP_interpreter_optimized_ops,

			ILOP_iadd4,
			ILOP_iadd8,
			ILOP_isub4,
			ILOP_isub8,
			ILOP_imul4,
			ILOP_imul8,
			ILOP_idiv4,
			ILOP_idiv8,
			ILOP_imod4,
			ILOP_imod8,
			ILOP_ineg4,
			ILOP_ineg8,

			ILOP_isx18,
			ILOP_isx28,
			ILOP_isx48,
			ILOP_isx14,
			ILOP_isx24,
			ILOP_isx12,

			ILOP_ict84,
			ILOP_ict82,
			ILOP_ict81,
			ILOP_ict42,
			ILOP_ict41,
			ILOP_ict21,
			
			// Null conversions
			ILOP_nconv8,
			ILOP_nconv4,
			ILOP_nconv2,
			ILOP_nconv1,

			ILOP_itof44,
			ILOP_itof48,
			ILOP_itof84,
			ILOP_itof88,

			ILOP_ilt4,
			ILOP_ilt8,
			ILOP_igt4,
			ILOP_igt8,
			ILOP_ile4,
			ILOP_ile8,
			ILOP_ige4,
			ILOP_ige8,
			ILOP_ieq4,
			ILOP_ieq8,
			ILOP_ine4,
			ILOP_ine8,

			ILOP_fadd4,
			ILOP_fsub4,
			ILOP_fmul4,
			ILOP_fdiv4,
			ILOP_fneg4,

			ILOP_ftoi44,
			ILOP_ftoi48,
			ILOP_ftoi84,
			ILOP_ftoi88,
			
			ILOP_flt4,
			ILOP_fgt4,
			ILOP_fle4,
			ILOP_fge4,
			ILOP_feq4,
			ILOP_fne4,

			ILOP_iteratearray4,
			ILOP_iteratearray8,
			ILOP_iteratearraysub4,
			ILOP_iteratearraysub8,

			ILOP_zero1,
			ILOP_zero2,
			ILOP_zero4,
			ILOP_zero8,

			ILOP_immediate1,
			ILOP_immediate2,
			ILOP_immediate4,
			ILOP_immediate8,

			ILOP_jeq_f1,
			ILOP_jeq_f2,
			ILOP_jeq_f4,
			ILOP_jeq_f8,

			ILOP_jeq_p1,
			ILOP_jeq_p2,
			ILOP_jeq_p4,
			ILOP_jeq_p8,

			ILOP_jne_f1,
			ILOP_jne_f2,
			ILOP_jne_f4,
			ILOP_jne_f8,

			ILOP_jne_p1,
			ILOP_jne_p2,
			ILOP_jne_p4,
			ILOP_jne_p8,

			ILOP_move1,
			ILOP_move2 = ILOP_move1 + NUM_OPTIMIZED_MOVES,
			ILOP_move4 = ILOP_move2 + NUM_OPTIMIZED_MOVES,
			ILOP_move8 = ILOP_move4 + NUM_OPTIMIZED_MOVES,
			ILOP_movertp = ILOP_move8 + NUM_OPTIMIZED_MOVES,
			ILOP_movesz = ILOP_movertp + NUM_OPTIMIZED_MOVES,

			ILOP_unused = ILOP_movesz + NUM_OPTIMIZED_MOVES,
		};
	}
}

#endif
